import Cesium, { Cartesian3, Cartographic, JulianDate, Ray, Scene } from '../../../Ces/Cesium';

// 定义 Terria 类型，这里假设它是一个具有 scene、trackedEntity 和 clock 属性的对象
interface Terria {
  scene: Scene;
  trackedEntity?: {
    position: {
      getValue: (time: JulianDate, result: Cartesian3) => Cartesian3;
    };
  };
  clock: {
    currentTime: JulianDate;
  };
}

const Utils: {
  getCameraFocus: (
    terria: Terria,
    inWorldCoordinates: boolean,
    result?: Cartesian3
  ) => Cartesian3 | undefined;
} = {} as any;

let unprojectedScratch: Cartographic;
let rayScratch: Ray;

/**
 * 获取相机的焦点
 * @param {Terria} terria The terria
 * @param {boolean} inWorldCoordinates 若为 true 则以世界坐标获取焦点，否则以特定投影的地图坐标（米）获取焦点
 * @param {Cesium.Cartesian3} [result] 用于存储结果的对象
 * @return {Cesium.Cartesian3} 修改后的结果参数，若未提供则为新实例，若没有焦点则为 undefined
 */
Utils.getCameraFocus = function (
  terria: Terria,
  inWorldCoordinates: boolean,
  result?: Cartesian3
): Cartesian3 | undefined {
  const scene = terria.scene;
  const camera = scene.camera;
  unprojectedScratch = new Cesium.Cartographic();
  rayScratch = new Cesium.Ray();

  if (scene.mode === Cesium.SceneMode.MORPHING) {
    return undefined;
  }

  if (!Cesium.defined(result)) {
    result = new Cesium.Cartesian3();
  }

  // TODO bug when tracking: if entity moves the current position should be used and not only the one when starting orbiting/rotating
  // TODO bug when tracking: reset should reset to default view of tracked entity

  if (Cesium.defined(terria.trackedEntity)) {
    result = terria.trackedEntity.position.getValue(
      terria.clock.currentTime,
      result
    );
  } else {
    rayScratch.origin = camera.positionWC;
    rayScratch.direction = camera.directionWC;
    result = scene.globe.pick(rayScratch, scene, result);
  }

  if (!Cesium.defined(result)) {
    return undefined;
  }

  if (
    scene.mode === Cesium.SceneMode.SCENE2D ||
    scene.mode === Cesium.SceneMode.COLUMBUS_VIEW
  ) {
    result = camera.worldToCameraCoordinatesPoint(result, result);

    if (inWorldCoordinates) {
      result = scene.globe.ellipsoid.cartographicToCartesian(
        scene.mapProjection.unproject(result, unprojectedScratch),
        result
      );
    }
  } else {
    if (!inWorldCoordinates) {
      result = camera.worldToCameraCoordinatesPoint(result, result);
    }
  }

  return result;
};

export default Utils;
